import { BaseCommand } from '../../core/command.js';
import type { CommandContext, GitHubRepository } from '../../types/index.js';
import { createSpinner } from '../../utils/spinner.js';
import { createNetworkManager } from '../../utils/network.js';
import { createFileManager } from '../../utils/file.js';
import { simpleGit } from 'simple-git';
import { join } from 'node:path';

export class GitHubPullCommand extends BaseCommand {
  name = 'github-pull';
  description = 'Pull a GitHub repository to local directory (Demo Command)';
  aliases = ['gh-pull', 'clone'];

  arguments = [
    {
      name: 'repository',
      description: 'GitHub repository URL or owner/repo format',
      required: true,
    },
    {
      name: 'destination',
      description: 'Destination directory (optional)',
      required: false,
    },
  ];

  options = [
    {
      name: 'branch',
      description: 'Specific branch to clone',
      type: 'string' as const,
      default: 'main',
    },
    {
      name: 'depth',
      description: 'Create a shallow clone with history truncated to specified number of commits',
      type: 'number' as const,
    },
    {
      name: 'single-branch',
      description: 'Clone only the specified branch',
      type: 'boolean' as const,
      default: false,
    },
  ];

  examples = [
    'genwavejs demo github-pull https://github.com/user/repo',
    'genwavejs demo github-pull user/repo ./my-project',
    'genwavejs demo github-pull user/repo --branch develop --depth 1',
  ];

  async execute(context: CommandContext): Promise<void> {
    this.validateArguments(context);
    this.validateOptions(context);

    const [repositoryInput, destination] = context.args;
    const { branch, depth, 'single-branch': singleBranch } = context.options;

    const spinner = createSpinner();
    const networkManager = createNetworkManager();
    const fileManager = createFileManager();

    try {
      // Parse repository information
      const repository = this.parseRepository(repositoryInput);
      context.logger.info(`Parsing repository: ${repository.url}`);

      // Validate repository exists
      spinner.start({ text: 'Validating repository...' });
      const isValid = await this.validateRepository(repository, networkManager);
      
      if (!isValid) {
        spinner.fail('Repository validation failed');
        throw new Error(`Repository not found or not accessible: ${repository.url}`);
      }
      
      spinner.succeed('Repository validated successfully');

      // Determine destination directory
      const destDir = destination || repository.repo;
      const fullDestPath = fileManager.resolvePath(context.cwd, destDir);

      // Check if destination exists
      if (await fileManager.exists(fullDestPath)) {
        const isEmpty = (await fileManager.readDir(fullDestPath)).length === 0;
        if (!isEmpty) {
          throw new Error(`Destination directory is not empty: ${fullDestPath}`);
        }
      }

      // Clone repository
      spinner.start({ text: `Cloning repository to ${destDir}...` });
      
      await this.cloneRepository(repository, fullDestPath, {
        branch: branch || 'main',
        depth: depth ? Number(depth) : undefined,
        singleBranch,
      });

      spinner.succeed(`Repository cloned successfully to ${destDir}`);

      // Display summary
      context.logger.success('GitHub Pull Demo completed successfully!');
      context.logger.info(`Repository: ${repository.url}`);
      context.logger.info(`Destination: ${fullDestPath}`);
      context.logger.info(`Branch: ${branch || 'main'}`);
      
      if (depth) {
        context.logger.info(`Depth: ${depth} commits`);
      }

    } catch (error) {
      spinner.fail('GitHub pull failed');
      context.logger.error('Error:', error instanceof Error ? error.message : String(error));
      throw error;
    }
  }

  private parseRepository(input: string): GitHubRepository {
    // Handle different input formats
    if (input.startsWith('https://github.com/') || input.startsWith('git@github.com:')) {
      // Full URL format
      const match = input.match(/github\.com[/:]([\w-]+)\/([\w-]+)(?:\.git)?/);
      if (!match) {
        throw new Error(`Invalid GitHub URL format: ${input}`);
      }
      
      return {
        owner: match[1],
        repo: match[2],
        url: `https://github.com/${match[1]}/${match[2]}.git`,
      };
    } else if (input.includes('/')) {
      // owner/repo format
      const [owner, repo] = input.split('/');
      if (!owner || !repo) {
        throw new Error(`Invalid repository format: ${input}. Expected: owner/repo`);
      }
      
      return {
        owner,
        repo,
        url: `https://github.com/${owner}/${repo}.git`,
      };
    } else {
      throw new Error(`Invalid repository format: ${input}. Expected: owner/repo or full GitHub URL`);
    }
  }

  private async validateRepository(repository: GitHubRepository, networkManager: any): Promise<boolean> {
    try {
      // Check if repository exists via GitHub API
      const apiUrl = `https://api.github.com/repos/${repository.owner}/${repository.repo}`;
      await networkManager.get(apiUrl);
      return true;
    } catch (error) {
      // Fallback: try to check the repository URL directly
      try {
        const repoUrl = `https://github.com/${repository.owner}/${repository.repo}`;
        return await networkManager.checkUrl(repoUrl);
      } catch {
        return false;
      }
    }
  }

  private async cloneRepository(
    repository: GitHubRepository,
    destination: string,
    options: {
      branch?: string;
      depth?: number;
      singleBranch?: boolean;
    }
  ): Promise<void> {
    const git = simpleGit();
    
    const cloneOptions: string[] = [];
    
    if (options.depth) {
      cloneOptions.push('--depth', options.depth.toString());
    }
    
    if (options.singleBranch) {
      cloneOptions.push('--single-branch');
    }
    
    if (options.branch && options.branch !== 'main') {
      cloneOptions.push('--branch', options.branch);
    }

    await git.clone(repository.url, destination, cloneOptions);
  }
}
